﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Cofe.Core.Property
{

    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class GetFirst : IPropertyGetBehavior
    {
        #region Constructor

        public GetFirst()
        {
        }

        #endregion

        #region Methods

        public async Task<PropertyPair[]> GetPropertyAsync(object property, IPropertyProvider[] allPropertyProviders,
            PropertyRetrivalOptions options)
        {
            if (options.HasFlag(PropertyRetrivalOptions.First) || options.HasFlag(PropertyRetrivalOptions.Default))
            {
                foreach (var pp in allPropertyProviders)
                    if (pp.IsPropertySupported(property))
                    {
                        var retVal = (await pp.GetPropertyAsync(property));
                        if (retVal != null && retVal.Any(ppair => ppair.Property.Equals(property)))
                            return retVal;
                    }

                if (this.PropertyDictionary.AttributeHelper.HasDefaultValue(property))
                    return this.PropertyDictionary.GetDefaultValuePP(property).ToArray();
            }
            return null;
        }

        #endregion

        #region Data

        private IPropertyDictionary _propertyDictionary = null;

        #endregion

        #region Public Properties

        public IPropertyDictionary PropertyDictionary
        {
            get { return _propertyDictionary ?? CofeServices.ServiceLocater.FindService<IPropertyDictionary>(); }
            set { _propertyDictionary = value; }
        }


        public PropertyRetrivalOptions SupportOption
        {
            get { return PropertyRetrivalOptions.Default | PropertyRetrivalOptions.First; }
        }

        #endregion



    }

    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class GetAggregated : IPropertyGetBehavior
    {
        #region Constructor

        public GetAggregated()
        {
        }

        #endregion

        #region Methods

        public async Task<PropertyPair[]> GetPropertyAsync(object property, IPropertyProvider[] allPropertyProviders,
            PropertyRetrivalOptions options)
        {

            if (options.HasFlag(PropertyRetrivalOptions.Aggregate))
            {
                var expr = (await Task.WhenAll(from pp in allPropertyProviders
                                               where pp.IsPropertySupported(property)
                                               select pp.GetPropertyAsync(property)));
                if (!(expr.Any()))
                {
                    if (this.PropertyDictionary.AttributeHelper.HasDefaultValue(property))
                        return this.PropertyDictionary.GetDefaultValuePP(property).ToArray();
                    return null;
                }

                return (expr
                    .Where(ppairs => ppairs.Any(pp => pp.Property.Equals(property)))
                    .Select(ppairs => ppairs.Find(property))
                    .Aggregate((first, second) => first + second)).ToArray();
            }
            return null;
        }

        #endregion

        #region Data

        private IPropertyDictionary _propertyDictionary = null;

        #endregion

        #region Public Properties

        public IPropertyDictionary PropertyDictionary
        {
            get { return _propertyDictionary ?? CofeServices.ServiceLocater.FindService<IPropertyDictionary>(); }
            set { _propertyDictionary = value; }
        }


        public PropertyRetrivalOptions SupportOption
        {
            get { return PropertyRetrivalOptions.Aggregate; }
        }

        #endregion
    }

    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class GetMinMax : IPropertyGetBehavior
    {
        #region Constructor

        public GetMinMax()
        {
        }

        #endregion

        #region Methods


        private int compare(PropertyPair value1, PropertyPair value2, PropertyRetrivalOptions options)
        {
            int modifier = options == PropertyRetrivalOptions.Min ? -1 : 0;
            Type type = DecoratorExtension.GetDefaultType(value1.Property);
            if (type == typeof(Int16) || type == typeof(Int32) || type == typeof(Int64))
                return value1.ValueAsInt64.CompareTo(value2.ValueAsInt64) * modifier;
            if (type == typeof(DateTime))
                return value1.ValueAs<DateTime>().CompareTo(value2.ValueAs<DateTime>()) * modifier;

            throw new NotSupportedException();
        }

        public async Task<PropertyPair[]> GetPropertyAsync(object property, IPropertyProvider[] allPropertyProviders,
            PropertyRetrivalOptions options)
        {
            if (options.HasFlag(PropertyRetrivalOptions.Max) || options.HasFlag(PropertyRetrivalOptions.Min))
            {
                var expr = (await Task.WhenAll(from pp in allPropertyProviders
                                               where pp.IsPropertySupported(property)
                                               select pp.GetPropertyAsync(property)));
                if (!(expr.Any()))
                {
                    if (this.PropertyDictionary.AttributeHelper.HasDefaultValue(property))
                        return this.PropertyDictionary.GetDefaultValuePP(property).ToArray();
                    return null;
                }

                return (expr
                    .Where(ppairs => ppairs.Any(pp => pp.Property.Equals(property)))
                    .Select(ppairs => ppairs.Find(property))
                    .Aggregate((first, second) => compare(first, second, options) > 0 ? first : second)).ToArray();
            }
            return null;
        }

        #endregion

        #region Data

        private IPropertyDictionary _propertyDictionary = null;

        #endregion

        #region Public Properties

        public IPropertyDictionary PropertyDictionary
        {
            get { return _propertyDictionary ?? CofeServices.ServiceLocater.FindService<IPropertyDictionary>(); }
            set { _propertyDictionary = value; }
        }


        public PropertyRetrivalOptions SupportOption
        {
            get { return PropertyRetrivalOptions.Min | PropertyRetrivalOptions.Max; }
        }

        #endregion
    }

    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class GetFirstHandled : IPropertyGetBehavior
    {
        #region Constructor

        public GetFirstHandled()
        {
        }

        #endregion

        #region Methods

        public async Task<PropertyPair[]> GetPropertyAsync(object property, IPropertyProvider[] allPropertyProviders,
            PropertyRetrivalOptions options)
        {
            if (options.HasFlag(PropertyRetrivalOptions.HandleOnes))
            {
                var expr = (await Task.WhenAll(from pp in allPropertyProviders
                                               where pp.IsPropertySupported(property)
                                               select pp.GetPropertyAsync(property)));
                if (!(expr.Any()))
                {
                    if (this.PropertyDictionary.AttributeHelper.HasDefaultValue(property))
                        return this.PropertyDictionary.GetDefaultValuePP(property).ToArray();
                    return null;
                }

                return (expr
                    .Where(ppairs => ppairs.Any(pp => pp.Property.Equals(property)))
                    .Select(ppairs => ppairs.Find(property))
                    .Aggregate((first, second) => first | second)).ToArray(); //Operator is Or, GetAggregateProperty is Add.
            }
            else return null;
        }

        #endregion

        #region Data

        private IPropertyDictionary _propertyDictionary = null;

        #endregion

        #region Public Properties

        public IPropertyDictionary PropertyDictionary
        {
            get { return _propertyDictionary ?? CofeServices.ServiceLocater.FindService<IPropertyDictionary>(); }
            set { _propertyDictionary = value; }
        }


        public PropertyRetrivalOptions SupportOption
        {
            get { return PropertyRetrivalOptions.HandleOnes; }
        }

        #endregion



    }


    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class GetCacheOnly : IPropertyGetBehavior
    {
        #region Constructor

        public GetCacheOnly()
        {
        }

        #endregion

        #region Methods

        public async Task<PropertyPair[]> GetPropertyAsync(object property, IPropertyProvider[] allPropertyProviders,
            PropertyRetrivalOptions options)
        {

            IPropertyCache propertyCache = allPropertyProviders.FirstOrDefault(pp => pp is IPropertyCache) as IPropertyCache;
            if (propertyCache == null)
                return null;

            PropertyPair retVal = null;

            if (propertyCache.IsPropertySupported(property))
                retVal = (await propertyCache.GetPropertyAsync(property)).FirstOrDefault(pp => pp.Property.Equals(property));

            if (retVal == null && this.PropertyDictionary.AttributeHelper.HasDefaultValue(property))
                retVal = this.PropertyDictionary.GetDefaultValuePP(property);
            return retVal.ToArray();
        }

        #endregion

        #region Data

        private IPropertyDictionary _propertyDictionary = null;

        #endregion

        #region Public Properties

        public IPropertyDictionary PropertyDictionary
        {
            get { return _propertyDictionary ?? CofeServices.ServiceLocater.FindService<IPropertyDictionary>(); }
            set { _propertyDictionary = value; }
        }


        public PropertyRetrivalOptions SupportOption
        {
            get { return PropertyRetrivalOptions.CacheOnly; }
        }

        #endregion



    }
}
